使用者應該要能輸入他們自己的資料,因此我們建置一個系統確定哪些資料屬於哪個使用者所有,然後限制某些頁面的存取權,以便讓使用者輸入自己的資料
Django提供了@login_required修飾模式來讓我們輕鬆實現這個目標
每個主題都是由使用者所建立擁有的,因此只有登入的使用者才能夠請求主題頁面,我們要打開learning_logs資料夾中的views.py來匯入login_required函式,再將login_required函式當作修飾模式用在topics視圖函式,如下
--省略--
from django.contrib.auth.decorators import login_required
--省略--
@login_required # 會檢測使用者是否已經登入,若已登入才會執行topics()的程式
def topics(request):
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
要做到重新導向到登入頁面,我們還得在setting.py的尾端新增以下程式碼
# my settings
LOGIN_URL='/users/login'
新增完後我們可以開啟我們的網頁來測試看看是不是登出後就沒辦法點進去Topics裡
在learning log專案中我們要對主頁、登入註冊頁面和登出頁面是不設限制的,但對其他都要設定存取權,所以我們要在learning logs資料夾中的views.py將@login_required套用到index()以外的每個視圖,如下
@login_required
def topics(request):
--省略--
@login_required
def topic(request, topic_id):
--省略--
@login_required
def new_topic(request):
--省略--
@login_required
def new_entry(request, topic_id):
--省略--
@login_required
def edit_entry(request, entry_id):
--省略--
現在我們要把資料連接到提交他們的使用者上,我們只需要把最高級層的資料連接到使用者,這樣低級層的資料就會跟著連接到所屬的使用者
我們要來修改Topic模型,在其中新增一個外部鍵連接使用者,隨後我們會修改遷移資料庫,最後還必須對視圖做一些修改,讓它們只顯示與目前登入使用者相關有連接的資料,我們要開啟models.py來新增一些程式碼,如下
from django.db import models
from django.contrib.auth.models import User # 新增區塊
class Topic(models.Model):
--省略--
owner=models.ForeignKey(User,on_delete=models.CASCADE) # 新增區塊
--省略--
我們從執行python manage.py makemigrations learning_logs
命令後,Django會給我們兩個選擇,馬上提供預設值,或是退出並在models.py中新增預設值,在這裡我們選了第一種,因此Django讓我們輸入預設值,為了要把所有現有的主題與原本管理者weiting2關聯起來,我們在這裡輸入1的使用者ID,如下
現在我們可以進行遷移了,我們要在虛擬環境下輸入python manage.py migrate
,如下
目前不管以哪個身分登入,都能看到所有主題,我們要來修改成限制存取屬於自己的主題,打開views.py對topics函式新增程式碼,如下
@login_required
def topics(request):
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
當使用者登入後,request物件會有個user屬性,此屬性儲存了關於該使用者的資訊,Topic.objects.filter(owner=request.user)這段程式碼會讓Django只從資料庫中取得其owner屬性為目前使用者的Topic物件
我們還沒真的限制存取主題頁面,所以任何已登入的使用者都可輸入類似像http://localhost:8000/topics/1 這樣的URL來存取對應的主題頁面,為了修改這樣的問題,我們在topics()視圖函式取得請求的紀錄項目之前先執行檢測,如下
--省略--
from django.http import HttpResponseRedirect,Http404
--省略--
@login_required
def topic(request, topic_id):
"""Show a single topic, and all its entries."""
topic = Topic.objects.get(id=topic_id)
if topic.owner != request.user: # 請求主題與現在使用者不符合
raise Http404 # 返回錯誤頁面
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
edit_entry頁面的URL格式為 http://localhost:8000/edit_entry/entry_id ,其中entry_id是個數字,讓我們來保護這個頁面,這樣任何人都不能以這個URL來存他人的紀錄項目,如下
@login_required
def edit_entry(request, entry_id):
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if topic.owner != request.user:
raise Http404
--省略--
目前我們新增主題頁面是有問題的,因為它還不會把新主題與任何特定使用者關聯起來,我們可以試著新增主題,會看到一個IntegrityError錯誤訊息,指出learning_logs_topic.user_id不能是NULL,因此我們可以透過request物件存取目前使用者,開啟views.py加入以下程式碼
@login_required
def new_topic(request):
if request.method != 'POST':
form = TopicForm()
else:
form = TopicForm(request.POST)
if form.is_valid():
new_topic = form.save(commit=False) # 新增區塊
new_topic.owner = request.user # 新增區塊
new_topic.save() # 新增區塊
return HttpResponseRedirect(reverse('learning_logs:topics'))
--省略--
第八行的地方我們是先呼叫form.save()並把commit=False當引數傳入,這是因為我們先修改新主題,再把它儲存進資料庫,隨後把新主題的OWNER屬性設為目前使用者,最後對剛定義的主題實例呼叫save()
附上排版較精美的
HackMD網址:https://hackmd.io/pdXIhZM6Q8yjwN3BjiJBKQ?both
資料來源:<<python程式設計的樂趣>>-Eric Matthes著/H&C譯